package org.brownflat.lighthouse.monitoring.ci.teamcity;

import org.apache.log4j.Logger;
import org.brownflat.lighthouse.monitoring.ci.Build;
import org.brownflat.lighthouse.monitoring.ci.Builder;
import org.brownflat.lighthouse.monitoring.ci.Feed;
import org.brownflat.lighthouse.monitoring.xml.XMLDocument;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class TeamCityBuilder implements Builder<Feed, String> {

    private static final Logger LOG = Logger.getLogger(TeamCityBuilder.class);

    private List<String> buildNames;

    private static HashMap<String, String> namespaceContexts;

    static {
        namespaceContexts = new HashMap<String, String>();
        namespaceContexts.put("atom", "http://www.w3.org/2005/Atom");
        namespaceContexts.put("dc", "http://purl.org/dc/elements/1.1/");
    }

    public TeamCityBuilder(String... buildNames) {
        this.buildNames = Arrays.asList(buildNames);
    }

    public TeamCityBuilder() {
        this.buildNames = new ArrayList<String>();
    }

    public Feed build(String input) {
        XMLDocument document = new XMLDocument(input);
        document.setNamespaceContexts(namespaceContexts);

        List<XMLDocument> entryDocuments = document.documentsAt("/atom:feed/child::atom:entry");
        Feed feed = new Feed();

        for (XMLDocument xmlDocument : entryDocuments) {
            Build build = buildBuild(xmlDocument);
            if (matchesSelectedBuilds(build)) {
                feed.addBuild(build);
            }
        }
        return feed;
    }

    private boolean matchesSelectedBuilds(Build build) {
        boolean match = false;

        if (buildNames.isEmpty()) {
            match = true;
        }

        for (String thisName : buildNames) {
            if (build.matchesPattern(thisName)) {
                match = true;
            } else {
                LOG.debug(String.format("Build did not match pattern [%s] %s", thisName, build));
            }
        }
        return match;
    }

    private Build buildBuild(XMLDocument xmlDocument) {
        Build entry = new Build();
        entry.setName(getName(xmlDocument.selectValueAt("/atom:entry/atom:title")));
        entry.setProject(getProject(xmlDocument.selectValueAt("/atom:entry/atom:title")));
        entry.setBuildNumber(getBuildNumber(xmlDocument.selectValueAt("/atom:entry/atom:title")));
        entry.setRawData(xmlDocument.selectValueAt("/atom:entry/*"));
        entry.setBroken(xmlDocument.selectValueAt("/atom:entry/atom:author/atom:name").contains("Failed Build"));
        entry.setDate(getDate(xmlDocument.selectValueAt("/atom:entry/dc:date")));
        return entry;
    }

    private String buildMatcher(String title, int index) {
        Pattern outerPattern = Pattern.compile("^Build (.*)::(.*) #(.*) (was|has).*$");
        Matcher matcher = outerPattern.matcher(title);
        if(!matcher.matches()) {
            return null;
        } else {
            try {
                return matcher.group(index);
            } catch (IndexOutOfBoundsException iooe) {
                LOG.warn(String.format("Couldn't find group index [%s] in [%s]", index, title), iooe);
                return "";
            }
        }
    }

    protected String getBuildNumber(String title) {
        String result = buildMatcher(title, 3);
        if(result == null) {
            LOG.warn(String.format("Couldn't find buildNumber in pattern from [%s]", title));
            return "";
        }
        return result.trim();
    }

    protected String getName(String title) {
        String result = buildMatcher(title, 2);
        if(result == null) {
            LOG.warn(String.format("Couldn't find build name in pattern from [%s]", title));
            return "";
        }
        return result.trim();
   }

    protected String getProject(String title) {
        String result = buildMatcher(title, 1);
        if(result == null) {
            LOG.warn(String.format("Couldn't find project name in pattern from [%s]", title));
            return null;
        }
        return result.trim();
    }

    private DateTime getDate(String dateValue) {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
        try {
            Date date = fmt.parse(dateValue);
            return new DateTime(date, DateTimeZone.UTC);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}
